package com.scalable.io.nio.basic.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * <p>基于nio的reactor模型</p>
 * Single threaded version
 */
public class Reactor implements Runnable {

    private final ServerSocketChannel serverSocketChannel;
    /*事件轮询器   对select、poll、epoll封装*/
    private final Selector selector;
    private final static int PORT = 8081;

    public Reactor() throws IOException {
        selector = Selector.open(); // 打开一个Selector
        serverSocketChannel = ServerSocketChannel.open(); //建立一个 Server 端通道
        serverSocketChannel.socket().bind(new InetSocketAddress(PORT)); // 绑定服务端口
        serverSocketChannel.configureBlocking(false);  // selector模式下，所有通道必须是非阻塞的
        SelectionKey sk = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        // Reactor是入口，最初给一个channel注册上去的事件都是accept
        System.out.println("start server accept " + serverSocketChannel.getLocalAddress());
        //attach callback object, Acceptor
        sk.attach(new Acceptor(selector, serverSocketChannel));
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                int key = selector.select(); //就绪事件到达之前，阻塞
                if (key == 0) continue;
                //拿到本次select获取的就绪事件
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                while (it.hasNext()) {
                    dispacher(it.next());  //这里进行任务分发
                }
                selectionKeys.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (serverSocketChannel != null) serverSocketChannel.close();
                if (selector != null) selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void dispacher(SelectionKey key) {
        // 这里很关键，拿到每次selectKey里面附带的处理对象，然后调用其run，
        // 这个对象在具体的Handler里会进行创建，初始化的附带对象为Acceptor（看上面构造器）
        Runnable r = (Runnable) key.attachment();
        // 调用之前注册的callback对象
        if (r != null) r.run(); //只是拿到句柄执行run方法，并没有新起线程

    }
}
